#include "NetworkClient.hpp"
#include <iostream>

namespace phx
{

static void clientLoop(NetworkClient *client)
{
	while(client->isStarted())
	{
		client->receive();
	}
	std::this_thread::yield();
}

NetworkClient::NetworkClient(const char *serverAddress, unsigned short serverPort, NetworkType networkType) : 
	mSocket(UDT::socket(AF_INET, (networkType == NetworkType::Reliable ? SOCK_DGRAM : SOCK_STREAM), 0)),
	mNetworkType(networkType),
	mStarted(false),
	mThread(nullptr)
{
	memset(&mServerAddr, 0, sizeof(mServerAddr));
	mServerAddr.sin_family = AF_INET;
	mServerAddr.sin_port = htons(serverPort);
	//mServerAddr.sin_addr.s_addr = INADDR_BROADCAST;
	inet_pton(AF_INET, serverAddress, &mServerAddr.sin_addr);

	//memset(&(mServerAddr.sin_zero), '\0', 8);
	bool block = false;
	int blockSet = UDT::setsockopt(mSocket, 0, UDT_RCVSYN, (char*)&block, sizeof(bool));
	if(UDT::connect(mSocket, (sockaddr*)&mServerAddr, sizeof(mServerAddr)) == UDT::ERROR)
	{
		std::cout << "bind: " << UDT::getlasterror().getErrorMessage()<<std::endl;
		// further action may depends on UDT::getlasterror().getErrorCode()
	}
	else
	{
		mStarted = true;
		mThread = new std::thread(&clientLoop, this);
		mThread->detach();
	}
}

void NetworkClient::send(const char *data, int size)
{
	switch(mNetworkType)
	{
		case NetworkType::Reliable:
			UDT::sendmsg(mSocket, data, size, -1, true);
			break;
		case NetworkType::Unreliable:
			UDT::send(mSocket, data, size, 0);
			break;
	}
}

void NetworkClient::send(Packet &packet)
{
	switch(mNetworkType)
	{
		case NetworkType::Reliable:
			UDT::sendmsg(mSocket, packet.mData.c_str(), packet.mData.size(), -1, true);
			break;
		case NetworkType::Unreliable:
			UDT::send(mSocket, packet.mData.c_str(), packet.mData.size(), 0);
			break;
	}
}

void NetworkClient::receive()
{
	int rcv_size;
	int var_size = sizeof(int);
	int resultInfo;
	UDT::getsockopt(mSocket, 0, UDT_RCVDATA, &rcv_size, &var_size);
	switch(mNetworkType)
	{
		case NetworkType::Reliable:
			resultInfo = UDT::recvmsg(mSocket, mReceivedData, 255);
			break;
		case NetworkType::Unreliable:
			resultInfo = UDT::recv(mSocket, mReceivedData, 255, 0);
			break;
	}
	if(resultInfo != UDT::ERROR && rcv_size > 0)
	{
		char *d = new char[resultInfo];
		memcpy(d, mReceivedData, resultInfo);
		// The packet has been given the resposibility of deleting "d"
		Packet packet;
		packet.setData(d, resultInfo);

		std::string eventName;
		packet.extract(eventName);
		Event::trigger(eventName, packet);
	}
}

NetworkClient::~NetworkClient()
{
	if(mThread)
	{
		mStarted = false;
		delete mThread;
	}
	UDT::close(mSocket);
}

}